<?php

function sandbox($code) {
	$tokens = token_get_all($code);
	$sandbox = '';
	$last = 0;
	foreach ($tokens as $i=>&$token) {
		for ($next = $i+1; $i < count($tokens) && in_array($tokens[$next][0], array(T_WHITESPACE, T_COMMENT)); $next++);
		$next = $tokens[$next];

		if (is_string($token)) {
			$sandbox .= $token;
		} elseif ($token[0] != T_STRING || $last == T_OBJECT_OPERATOR || $next != '(' || sandbox_allowed($token[1])) {
			$sandbox .= $token[1];
		} else {
			trigger_error("Function '$token[1]' is not accessible in the sandbox", E_USER_ERROR);
		}
		$last = $token[0];
	}
	return $sandbox;
}

function sandbox_allowed($callback, $value = NULL) {
	static $security = array();
	if (func_num_args() > 1) return $security["~^(?:$callback)$~i"] = $value;
	foreach ($security as $token => $value) if (preg_match($token, $callback)) return $value;
	return false;
}
function sandbox_allowed_functions() {
	$funcs = &get_defined_functions();
	$allowed = array();
	foreach ($funcs as &$list) foreach ($list as &$func) if (sandbox_allowed($func)) $allowed[] = $func;
	return $allowed;
}

function allow($callback, $value = true) {
	sandbox_allowed($callback, $value);
}

allow('get_.+|str.+|substr.*|json_.+|html.+|.+?_exists|is_.+|each|defined|extension_loaded|constant|bin2hex|time_nanosleep|time_sleep_until|flush|wordwrap|sha1|md5|crc32|iptcparse|iptcembed|getimagesize.*|image_type_to_mime_type|image_type_to_extension|phpinfo|phpversion|phpcredits|hebrev|hebrevc|nl2br|basename|dirname|pathinfo|quotemeta|ucfirst|lcfirst|ucwords|addslashes|addcslashes|rtrim|count_chars|chunk_split|trim|ltrim|similar_text|explode|implode|join|localeconv|soundex|levenshtein|chr|ord|parse_str|chop|sprintf|printf|vprintf|vsprintf|fprintf|vfprintf|sscanf|fscanf|parse_url|urlencode|urldecode|rawurlencode|rawurldecode|http_build_query|readlink|linkinfo|symlink|link|unlink|exec|system|escapeshellcmd|escapeshellarg|passthru|shell_exec|proc_open|proc_close|proc_terminate|proc_rand|srand|getrandmax|mt_rand|mt_srand|mt_getrandmax|getservbyname|getservbyport|getprotobyname|getprotobynumber|getmyuid|getmygid|getmypid|getmyinode|getlastmod|base64_decode|base64_encode|convert_uuencode|convert_uudecode|inet_ntop|inet_pton|ip2long|long2ip|microtime|gettimeofday|uniqid|quoted_printable_decode|quoted_printable_encode|convert_cyr_string|serialize|unserialize|var_dump|print_r|show_source|highlight_string|php_strip_whitespace|header|header_remove|headers_sent|headers_list|connection_aborted|connection_status|parse_ini_string|gethostbyaddr|gethostbyname|gethostbynamel|gethostname|dns_check_record|checkdnsrr|dns_getmxrr|intval|floatval|doubleval|gettype|pack|unpack|crypt|ezmlm_hash|lcg_value|metaphone|ksort|krsort|natsort|natcasesort|asort|arsort|sort|rsort|usort|uasort|uksort|shuffle|count|end|prev|next|reset|current|key|in_array|range|pos|sizeof|abs|acos|acosh|asin|asinh|atan2|atan|atanh|base_convert|bindec|ceil|cos|cosh|decbin|dechex|decoct|deg2rad|exp|expm1|floor|fmod|getrandmax|hexdec|hypot|lcg_value|log10|log1p|log|max|min|mt_getrandmax|mt_rand|mt_srand|octdec|pi|pow|rad2deg|rand|round|sin|sinh|sqrt|srand|tan|tanh|image(?!createfrom).+');
